package user

import (
	"fmt"
	"github.com/emicklei/go-restful"
	"kubeInstall/pkg/auth/jwtauth"
	"kubeInstall/pkg/logger"
	"kubeInstall/server/handlers"
	user2 "kubeInstall/server/models/user"
	"kubeInstall/server/services/user"
	"kubeInstall/utils"
	"kubeInstall/utils/validate"
	"net/http"
	"strconv"
)

type userHandler struct {
	handlers.BaseHandler
}

func NewUserHandler() handlers.IBaseHandler {
	return new(userHandler)
}

func (h *userHandler) AddToRoute(ws *restful.WebService) {

	ws.Route(ws.GET("/user/info").To(h.getUser))
	// list user
	ws.Route(ws.GET("/user").To(h.listUser))
	// delete user
	ws.Route(ws.DELETE("/user/{user_id}").To(h.deleteUser))
	// update user
	//ws.Route(ws.PUT("/user/{user_id}").To(handlers.UpdateUser))
	// change user password
	ws.Route(ws.PUT("/user/{user_id}/password").To(h.changeUserPassword))
	ws.Route(ws.POST("/logout").To(h.logout))
	logger.Info("add user module successfully")
}

func  AddLoginToRoute(ws *restful.WebService) {
	userHandler := new(userHandler)
	ws.Route(ws.POST("/login").To(userHandler.login))
	ws.Route(ws.POST("/register").To(userHandler.registerUser))
	logger.Info("add auth module successfully")
}

func (h *userHandler) getUser(r *restful.Request, resp *restful.Response) {

	token := r.HeaderParameter("Authorization")

	userid, err := jwtauth.Auth.ParseUserID(token)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("Parse user id failed! error is:%v", err), http.StatusBadRequest)
		return
	}

	data, err := user.GetUser(userid)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("get user failed! error is:  %v", err), http.StatusNotFound)
		return
	}
	data.Password = ""

	h.RenderResp(resp, data, http.StatusOK)

}

func (h *userHandler) updateUser(r *restful.Request, resp *restful.Response) {

	userId := r.PathParameter("user_id")
	if userId == "" {
		h.RenderError(resp, fmt.Sprintf("get user id from http query failed!"), http.StatusBadRequest)
		return
	}

	var req user2.User

	if err := r.ReadEntity(&req); err != nil {
		h.RenderError(resp, fmt.Sprintf("read update info failed. error is:  %v", err), http.StatusBadRequest)
		return
	}

	err := user.UpdateUser(userId, &req)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("update user failed! error is:  %v", err), http.StatusBadRequest)
		return
	}

	data, err := user.GetUser(userId)
	if err != nil {
		h.RenderError(resp, fmt.Sprintf("get user failed after update it ! error is:  %v", err), http.StatusBadRequest)
		return
	}
	data.Password = ""
	h.RenderResp(resp, data, http.StatusOK)

}

func (h *userHandler) modifiable(userId, token string, ) bool {

	claims, err := jwtauth.Auth.ParseToken(token)

	if err != nil {
		return false
	}

	return claims.IsAdmin || claims.Subject == userId

}

func (h *userHandler) changeUserPassword(r *restful.Request, resp *restful.Response) {
	token := r.HeaderParameter("Authorization")
	userId := r.PathParameter("user_id")
	if userId == "" {
		h.RenderError(resp, fmt.Sprintf("get user id from http query failed!"), http.StatusBadRequest)
		return
	}

	if !h.modifiable(userId, token) {
		h.RenderError(resp, fmt.Sprintf("User with ID: %s is not modifiable", userId), http.StatusForbidden)
		return
	}

	var req user2.PasswordReq

	if err := r.ReadEntity(&req); err != nil {
		h.RenderError(resp, fmt.Sprintf("read update info failed. error is:  %v", err), http.StatusBadRequest)
		return
	}

	if err := validate.ValidateSecret(req.NewPassword); err != nil {
		h.RenderError(resp, fmt.Sprintf("NewPassword : %s is invalid,error is:  %v ", req.NewPassword, err), http.StatusBadRequest)
		return
	}

	if len(req.OldPassword) == 0 {
		h.RenderError(resp, fmt.Sprintf("NewPassword : %s is empty ", req.NewPassword), http.StatusBadRequest)
		return
	}

	data, err := user.GetUser(userId)
	if err != nil {
		h.RenderError(resp, fmt.Sprintf("get user failed ! error is:  %v", err), http.StatusInternalServerError)
		return
	}
	if data == nil {
		h.RenderError(resp, fmt.Sprintf("user : %s is not found ", userId), http.StatusNotFound)
		return
	}

	if data.Password != utils.Encrypt(req.OldPassword, data.Salt, data.PasswordVersion) {
		h.RenderError(resp, fmt.Sprintf("User old password : %s is not correct", req.OldPassword), http.StatusForbidden)
		return
	}

	if data.Password == utils.Encrypt(req.NewPassword, data.Salt, data.PasswordVersion) {
		h.RenderError(resp, fmt.Sprintf("the new password :%s is same with the old one", req.NewPassword), http.StatusBadRequest)
		return
	}

	updatedUser := user2.User{
		Password: req.NewPassword,
	}

	err = user.ChangeUserPassword(userId, &updatedUser)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("update user failed! error is:  %v", err), http.StatusInternalServerError)
		return
	}

	result := handlers.SuccessResp{
		StatusCode: http.StatusOK,
		Message:    fmt.Sprintf("Change passowrd of %s Success", userId),
	}

	h.RenderResp(resp, result, http.StatusOK)

}

func (h *userHandler) registerUser(r *restful.Request, resp *restful.Response) {

	var req user2.User
	if err := r.ReadEntity(&req); err != nil {
		h.RenderError(resp, fmt.Sprintf("read register info failed. error is: %v", err), http.StatusBadRequest)
		return
	}

	if err := h.validateUser(req); err != nil {
		h.RenderError(resp, fmt.Sprintf("validate register info failed. error is: %v", err), http.StatusBadRequest)
		return
	}

	data, err := user.RegisterUser(&req)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("register user failed. error is: %v", err), http.StatusInternalServerError)
		return
	}

	logger.Info("register user %s successfully", data.ID)
	data.Password = ""
	h.RenderResp(resp, data, http.StatusOK)

}

func (h *userHandler) validateUser(user user2.User) error {
	if user.Username == "" || user.Password == "" {
		return fmt.Errorf("username ,password , email address can not be null")
	}

	if validate.IsIllegalLength(user.Username, 1, 255) {
		return fmt.Errorf("username with illegal length")
	}
	if validate.IsContainIllegalChar(user.Username, []string{",", "~", "#", "$", "%", "@"}) {
		return fmt.Errorf("username contains illegal characters")
	}

	if err := validate.ValidateSecret(user.Password); err != nil {
		return err
	}

	if user.Email != "" {
		return validate.ValidateEmail(user.Email)
	}

	return nil
}

func (h *userHandler) deleteUser(r *restful.Request, resp *restful.Response) {

	userid := r.PathParameter("user_id")
	if userid == "" {
		h.RenderError(resp, " user id not found", http.StatusBadRequest)
		return
	}

	err := user.DeleteUser(userid)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("delete user failed,error is: %v", err), http.StatusNotFound)
		return
	}
	logger.Info("delete user %s successfully", userid)
	data := handlers.SuccessResp{
		StatusCode: http.StatusOK,
		Message:    fmt.Sprintf("Delete %s Success", userid),
	}

	h.RenderResp(resp, data, http.StatusOK)
}

func (h *userHandler) listUser(r *restful.Request, resp *restful.Response) {

	offset, _ := strconv.Atoi(r.QueryParameter("offset"))
	limit, _ := strconv.Atoi(r.QueryParameter("limit"))

	data, err := user.ListUsers(limit, offset)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("list user failed,error is: %v", err), http.StatusBadRequest)
		return
	}
	logger.Info("list user successfully")
	h.RenderResp(resp, data, http.StatusOK)

}

func (h *userHandler) login(r *restful.Request, resp *restful.Response) {

	var req user2.User
	if err := r.ReadEntity(&req); err != nil {
		h.RenderError(resp, fmt.Sprintf("read login info failed,error is: %v", err), http.StatusBadRequest)
		return
	}

	u, err := user.LoginUser(&req)

	if err != nil {
		h.RenderError(resp, fmt.Sprintf("user login failed, error is :%v", err), http.StatusBadRequest)
		return
	}

	logger.Info("user %s login successfully", u.ID)

	tokenInfo, err := jwtauth.Auth.GenerateToken(u.ID, u.IsAdmin)
	if err != nil {
		h.RenderError(resp, fmt.Sprintf("Error while signing the token, error is :%v", err), http.StatusInternalServerError)
		return
	}

	h.RenderResp(resp, tokenInfo, http.StatusOK)
}

func (h *userHandler) logout(r *restful.Request, resp *restful.Response) {
	token := r.HeaderParameter("Authorization")

	username, err := jwtauth.Auth.DestroyToken(token)
	if err != nil {
		h.RenderError(resp, fmt.Sprintf("user logout failed!"), http.StatusInternalServerError)
		return
	}
	logger.Info("user %s logout successfully", username)
}
